#include "cardtable.h"
#include <random>
#include <QGraphicsScene>

CardTable::CardTable():QObject(nullptr), QGraphicsItem()
{
    is_playing_game = false;
    setFlag(ItemClipsChildrenToShape, true);

    card_pick_up = new QSoundEffect();
    card_pick_up->setSource(QUrl::fromLocalFile(":/sounds/card_pickup.wav"));
    card_pick_up->setVolume(0.6);
    card_place = new QSoundEffect();
    card_place->setSource(QUrl::fromLocalFile(":/sounds/card_place.wav"));
    card_place->setVolume(0.6);

    background = new QGraphicsPixmapItem(QPixmap(":/textures/table_large.png"), this);
    background->setTransformationMode(Qt::SmoothTransformation);
    background->setPos(0, 0);

    BonusCollectButton *red_button = new BonusCollectButton(Red, this);
    BonusCollectButton *green_button = new BonusCollectButton(Green, this);
    BonusCollectButton *white_button = new BonusCollectButton(White, this);
    red_button->setPos(QRectF(497, 18, 72, 71).topLeft());
    green_button->setPos(QRectF(497, 102, 72, 71).topLeft());
    white_button->setPos(QRectF(497, 184 , 72, 71).topLeft());
    bonus_collect_buttons[Red] = red_button;
    bonus_collect_buttons[Green] = green_button;
    bonus_collect_buttons[White] = white_button;

    connect(red_button, &BonusCollectButton::clicked, this, &CardTable::slotBonusButtonClicked);
    connect(green_button, &BonusCollectButton::clicked, this, &CardTable::slotBonusButtonClicked);
    connect(white_button, &BonusCollectButton::clicked, this, &CardTable::slotBonusButtonClicked);

    for (int i = 1; i <= SUITED_COUNT; ++i)
    {
        Card *coinsCard = new Card(Coins, i, this);
        Card *bambooCard = new Card(Bamboo, i, this);
        Card *charactersCard = new Card(Characters, i, this);
        source_cards.push_back(coinsCard);
        source_cards.push_back(bambooCard);
        source_cards.push_back(charactersCard);
    }
    for (int i = 0; i < BONUS_COUNT; ++i)
    {
        Card *whiteCard = new Card(White, i, this);
        Card *greenCard = new Card(Green, i, this);
        Card *redCard = new Card(Red, i, this);
        source_cards.push_back(whiteCard);
        source_cards.push_back(greenCard);
        source_cards.push_back(redCard);
    }
    Card *flowerCard = new Card(Flower, 0, this);
    source_cards.push_back(flowerCard);

    flower_card = nullptr;

    is_pressed = false;
    is_playing_animations = false;

    QPointF offset(0, -500);
    for (int i = 0; i < source_cards.size(); ++i)
    {
        source_cards[i]->setPos(cardSlotPosition({FlowerSlot, 0}) + offset);
    }

    timer_id = startTimer(1000 / 60); // 60FPS
}

CardTable::~CardTable()
{
    card_pick_up->deleteLater();
    card_place->deleteLater();
}

void CardTable::startNewGame()
{
    for (int i = 0; i < active_animation_groups.size(); ++i)
    {
        active_animation_groups[i]->deleteLater();
    }
    active_animation_groups.clear();
    is_playing_animations = false;

    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        playing_slots[i].clear();
    }
    for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
    {
        bonus_slots[i].clear();
    }
    for (int i = 0; i < SUITED_SLOTS_COUNT; ++i)
    {
        suited_slots[i].clear();
    }
    flower_card = nullptr;
    suited_card_counter.reset();

    std::vector<Card*> cards;
    cards.assign(source_cards.begin(), source_cards.end());
    shuffle(cards);
    for (int i = 0; i < cards.size(); ++i)
    {
        cards[i]->setZValue(i);
        cards[i]->flipToBack();
        cards[i]->setPos(cardSlotPosition({FlowerSlot, 0}));
    }
    update();
    for (int i = cards.size() - 1; i >= 0; --i)
    {
        playing_slots[7 - i % 8].push_back(cards[i]);
    }

    ParallelAnimationGroup *group = new ParallelAnimationGroup();
    group->setDelay(80);
    for (int i = cards.size() - 1; i >= 0; --i)
    {
        int j = cards.size() - i - 1;
        Animation *anim = moveAnimation(cards[i],  playingCardPosition(j % 8, j / 8),
                                        true, tweeny::easing::enumerated::quadraticOut, 300);
        anim->setStartZValue(40 + j);
        group->addAnimation(anim);
    }
    connect(group, &ParallelAnimationGroup::finished, this, [this, group](){
        releaseAnimationGroup(group);
        autoCollect();
    });

    bonus_collect_buttons[Red]->setStatus(BonusCollectButton::Up);
    bonus_collect_buttons[Green]->setStatus(BonusCollectButton::Up);
    bonus_collect_buttons[White]->setStatus(BonusCollectButton::Up);

    QTimer::singleShot(125, this, [this, group]{
        startAnimationGroup(group);
    });
    is_playing_game = true;
}

json CardTable::getPlayingData()
{
    json data = nullptr;
    QMap<QString, int> indexs;
    for (int i = 0; i < source_cards.size(); ++i)
    {
        indexs[source_cards[i]->str()] = i;
    }
    if (is_playing_game)
    {
        std::vector<std::vector<int>> playingCards;
        for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
        {
            std::vector<int> slotCards;
            for (int j = 0; j < playing_slots[i].size(); ++j)
            {
                slotCards.push_back(indexs[playing_slots[i][j]->str()]);
            }
            playingCards.push_back(slotCards);
        }
        data["playing_slots"] = playingCards;

        std::vector<std::vector<int>> bonusCards;
        for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
        {
            std::vector<int> slotCards;
            for (int j = 0; j < bonus_slots[i].size(); ++j)
            {
                slotCards.push_back(indexs[bonus_slots[i][j]->str()]);
            }
            bonusCards.push_back(slotCards);
        }
        data["bonus_slots"] = bonusCards;

        std::vector<std::vector<int>> suitedCards;
        for (int i = 0; i < SUITED_SLOTS_COUNT; ++i)
        {
            std::vector<int> slotCards;
            for (int j = 0; j < suited_slots[i].size(); ++j)
            {
                slotCards.push_back(indexs[suited_slots[i][j]->str()]);
            }
            suitedCards.push_back(slotCards);
        }
        data["suited_slots"] = suitedCards;

        if (flower_card == nullptr)
        {
            data["flower_card"] = nullptr;
        }
        else
        {
            data["flower_card"] = indexs[flower_card->str()];
        }
    }
    return data;
}

void CardTable::initializeByPlayingData(const json &data)
{
    if (data == nullptr) return;

    const json& playing = data["playing_slots"];
    for (int i = 0; i < playing.size(); ++i)
    {
        for (int j = 0; j < playing[i].size(); ++j)
        {
            Card *card = source_cards[playing[i][j]];
            card->setPos(playingCardPosition(i, j));
            card->setZValue(j);
            playing_slots[i].push_back(card);
        }
    }

    const json& bonus = data["bonus_slots"];
    for (int i = 0; i < bonus.size(); ++i)
    {
        int size = bonus[i].size();
        for (int j = 0; j < size; ++j)
        {
            Card *card = source_cards[bonus[i][j]];
            if (size == BONUS_COUNT)
            {
                card->flipToBack();
                if (j == 0)
                {
                    bonus_collect_buttons[card->cardType()]->setStatus(BonusCollectButton::Down);
                }
            }
            card->setPos(cardSlotPosition({BonusSlot, i}));
            card->setZValue(j); // TODO
            bonus_slots[i].push_back(card);
        }
    }

    const json& suited = data["suited_slots"];
    for (int i = 0; i < suited.size(); ++i)
    {
        for (int j = 0; j < suited[i].size(); ++j)
        {
            Card *card = source_cards[suited[i][j]];
            card->setPos(cardSlotPosition({SuitedSlot, i}));
            card->setZValue(j); //TODO
            suited_slots[i].push_back(card);
            if (!suited_card_counter.isNext(card->cardType(), card->cardNumber()))
            {
                suited_card_counter.manulPlace(card->cardType(), card->cardNumber());
            }
        }
    }

    const json& flower = data["flower_card"];
    if (flower != nullptr)
    {
        flower_card = source_cards[flower];
        flower_card->setPos(cardSlotPosition({FlowerSlot, 0}));
        flower_card->setZValue(0); // TODO
    }

    is_playing_game = true;
    autoCollect();
}

QRectF CardTable::boundingRect() const
{
    return QRectF(0, 0, background->boundingRect().width(), background->boundingRect().height());
}
void CardTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{}

void CardTable::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QPointF pos = event->scenePos();
    QGraphicsItem *item = scene()->itemAt(pos, QTransform());
    if (item != nullptr)
    {
        if (item == background) return;
        if (is_playing_animations) return;
        for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
        {
            if (bonus_slots[i].size() == 1)
            {
                if (item == bonus_slots[i][0])
                {
                    pressed_cards.clear();
                    pressed_cards.push_back(bonus_slots[i][0]);
                    moving_slot_type = BonusSlot;
                    moving_slot_index = i;
                    is_pressed = true;
                    begin_point = pos;
                    savePressedCards();
                    card_pick_up->play();
                    return;
                }
            }
        }

        for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
        {
            auto iter = std::find(playing_slots[i].begin(), playing_slots[i].end(), item);
            if (iter != playing_slots[i].end())
            {
                pressed_cards.assign(iter, playing_slots[i].end());
                if (pressed_cards.size() > 1)
                {
                    if (!moveable(pressed_cards))
                    {
                        pressed_cards.clear();
                        return;
                    }
                }
                moving_slot_type = PlayingSlot;
                moving_slot_index = i;
                is_pressed = true;
                begin_point = pos;
                savePressedCards();
                card_pick_up->play();
                return;
            }
        }
    }
}

void CardTable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (is_playing_animations) return;
    if (!is_pressed) return;
    QVector<CardSlot> cardSlots = placeableSlots();
    double maxArea = 0.0;
    int index = -1;
    for (int i = 0; i < cardSlots.size(); ++i)
    {
        QRectF r;
        switch (cardSlots[i].type)
        {
        case BonusSlot:
        case SuitedSlot:
            r = mapRectToScene(cardSlotRect(cardSlots[i]));
            break;
        case PlayingSlot:
        {
            if (playing_slots[cardSlots[i].index].size() == 0)
            {
                r = mapRectToScene(cardSlotRect(cardSlots[i]));
            }
            else
            {
                r = playing_slots[cardSlots[i].index].back()->sceneBoundingRect();
            }
        }
        break;
        default:
            continue;
        }
        double area = intersectArea(pressed_cards[0]->sceneBoundingRect(), r);
        if (area > maxArea)
        {
            maxArea = area;
            index = i;
        }
    }

    if (index != -1)
    {
        SlotType type = cardSlots[index].type;
        int slotIndex = cardSlots[index].index;
        if (type == PlayingSlot)
        {
            int start = playing_slots[slotIndex].size();
            for (int i = 0; i < pressed_cards.size(); ++i)
            {
                pressed_cards[i]->setPos(playingCardPosition(slotIndex, start + i));
                pressed_cards[i]->setZValue(40 + (start + i) * 8);
            }
            if (moving_slot_type == PlayingSlot)
            {
                moveCards(playing_slots[moving_slot_index], playing_slots[slotIndex], pressed_cards.size());
            }
            else if (moving_slot_type == BonusSlot)
            {
                moveCards(bonus_slots[moving_slot_index], playing_slots[slotIndex], pressed_cards.size());
            }
        }
        else if (type == BonusSlot)
        {
            pressed_cards[0]->setPos(cardSlotPosition({BonusSlot, slotIndex}));
            pressed_cards[0]->setZValue(1);
            if (moving_slot_type == PlayingSlot)
            {
                moveCards(playing_slots[moving_slot_index], bonus_slots[slotIndex], pressed_cards.size());
            }
            else
            {
                moveCards(bonus_slots[moving_slot_index], bonus_slots[slotIndex], pressed_cards.size());
            }
        }
        else if (type == SuitedSlot)
        {
            pressed_cards[0]->setPos(cardSlotPosition({SuitedSlot, slotIndex}));
            pressed_cards[0]->setZValue(79 + pressed_cards[0]->cardNumber() + suitedSlotIndex(pressed_cards[0]->cardType()));
            if (moving_slot_type == PlayingSlot)
            {
                moveCards(playing_slots[moving_slot_index], suited_slots[slotIndex], pressed_cards.size());
            }
            else
            {
                moveCards(bonus_slots[moving_slot_index], suited_slots[slotIndex], pressed_cards.size());
            }
            // 手动设置计数器
            suited_card_counter.manulPlace(pressed_cards[0]->cardType(), pressed_cards[0]->cardNumber());
        }
        autoCollect();
    }
    else
    {
        restorePressedCards();
    }
    is_pressed = false;
    card_place->play();
}

void CardTable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (is_playing_animations) return;
    if (is_pressed)
    {
        QPointF offset = event->scenePos() - begin_point;
        begin_point = event->scenePos();
        for (int i = 0; i < pressed_cards.size(); ++i)
        {
            pressed_cards[i]->setPos(pressed_cards[i]->pos() + offset);
        }
    }
}

void CardTable::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == timer_id)
    {
        for (int i = 0; i < active_animation_groups.size(); ++i)
        {
            active_animation_groups[i]->step();
        }
    }
}

void CardTable::savePressedCards()
{
    pressed_cards_saved_states.clear();
    for (int i = 0; i < pressed_cards.size(); ++i)
    {
        pressed_cards_saved_states.push_back(std::make_pair(pressed_cards[i]->pos(),
                                                            pressed_cards[i]->zValue()));
    }
    for (int j = 0; j < pressed_cards.size(); ++j)
    {
        pressed_cards[j]->setZValue(pressed_cards[j]->zValue() + 200);
    }
}

void CardTable::restorePressedCards()
{
    for (int i = 0; i < pressed_cards.size(); ++i)
    {
        pressed_cards[i]->setPos(pressed_cards_saved_states[i].first);
        pressed_cards[i]->setZValue(pressed_cards_saved_states[i].second);
    }
}

bool CardTable::moveable(std::vector<Card*> &cards)
{
    for (int i = 0; i < cards.size() - 1; ++i)
    {
        if (!cards[i + 1]->isDifferentTypeNext(cards[i]))
        {
            return false;
        }
    }
    return true;
}

bool CardTable::isFinished()
{
    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        if (!playing_slots[i].empty()) return false;
    }
    return true;
}

void CardTable::shuffle(std::vector<Card*> &cards)
{
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(cards.begin(), cards.end(), g);
}

QVector<CardTable::CardSlot> CardTable::placeableSlots()
{
    QVector<CardSlot> res;
    if (pressed_cards.size() == 1)
    {
        for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
        {
            if (bonus_slots[i].empty())
            {
                res.push_back({BonusSlot, i});
            }
        }

        for (int i = 0; i < SUITED_SLOTS_COUNT; ++i)
        {
            if (suited_slots[i].empty()) continue;
            Card *top = suited_slots[i].back();
            if (top->isSameTypeNext(pressed_cards[0]))
            {
                res.push_back({SuitedSlot, i});
            }
        }
    }
    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        if (moving_slot_type == PlayingSlot && i == moving_slot_index) continue;
        if (playing_slots[i].size() > 0)
        {
            Card *top = playing_slots[i].back();
            if (pressed_cards[0]->isDifferentTypeNext(top))
            {
                res.push_back({PlayingSlot, i});
            }
        }
        else
        {
            res.push_back({PlayingSlot, i});
        }
    }
    return res;
}

void CardTable::collectCard(const CardSlot &src, const CardSlot &dst, int startZValue, AnimationGroup *group)
{
    Card *card = nullptr;
    if (src.type == PlayingSlot && playing_slots[src.index].size() > 0)
    {
        card = playing_slots[src.index].back();
        playing_slots[src.index].pop_back();
    }
    if (src.type == BonusSlot && bonus_slots[src.index].size() == 1)
    {
        card = bonus_slots[src.index].back();
        bonus_slots[src.index].pop_back();
    }
    assert(card != nullptr);
    switch (dst.type) {
    case SuitedSlot:
        suited_slots[suitedSlotIndex(card->cardType())].push_back(card);
        break;
    case FlowerSlot:
        flower_card = card;
        break;
    case BonusSlot:
        bonus_slots[dst.index].push_back(card);
        break;
    default:
        break;
    }
    Animation *anim = moveAnimation(card, cardSlotPosition(dst),
                                    dst.type == BonusSlot ? true : false,
                                    tweeny::easing::enumerated::cubicOut);
    anim->setStartZValue(startZValue);
    group->addAnimation(anim);
}

void CardTable::autoCollect()
{
    bool changed = false;
    SequentialAnimationGroup *group = new SequentialAnimationGroup();
    do
    {
        changed = collectFlowerCard(group);
        changed = changed || collectSuitedCards(group);
        collectBonusCards();
    } while (changed);
    if (group->size() > 0)
    {
        connect(group, &SequentialAnimationGroup::finished, this, [this, group]{
            releaseAnimationGroup(group);
            if (isFinished())
            {
                emit gameFinished();
                slotGameFinished();
            }
        });
        startAnimationGroup(group);
    }
    else
    {
        delete group;
    }
}

bool CardTable::collectSuitedCards(SequentialAnimationGroup *group)
{
    bool changed = false;
    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        if (playing_slots[i].size() > 0)
        {
            Card *card = playing_slots[i].back();
            if (card->isSuitedCard())
            {
                if (suited_card_counter.isNext(card->cardType(), card->cardNumber()))
                {
                    int slotIndex = suitedSlotIndex(card->cardType());
                    int startZValue = 79 + card->cardNumber() + slotIndex;
                    collectCard({PlayingSlot, i}, {SuitedSlot, slotIndex}, startZValue, group);
                    changed = true;
                }
            }
        }
    }

    for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
    {
        if (bonus_slots[i].size() == 1)
        {
            Card *card = bonus_slots[i][0];
            if (card->isSuitedCard())
            {
                if (suited_card_counter.isNext(card->cardType(), card->cardNumber()))
                {
                    int slotIndex = suitedSlotIndex(card->cardType());
                    int startZValue = 79 + card->cardNumber() + slotIndex;
                    collectCard({BonusSlot, i}, {SuitedSlot, slotIndex}, startZValue, group);
                    changed = true;
                }
            }
        }
    }
    return changed;
}

bool CardTable::collectFlowerCard(SequentialAnimationGroup *group)
{
    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        if (playing_slots[i].size() > 0)
        {
            Card *top = playing_slots[i].back();
            if (top->isFlowerCard())
            {
                collectCard({PlayingSlot, i}, {FlowerSlot, 0}, 100, group);
                connect(group, &SequentialAnimationGroup::finished, this, [top](){
                    top->setZValue(0);
                });
                return true;
            }
        }
    }
    return false;
}

bool CardTable::collectBonusCards()
{
    bool changed = false;
    QMap<CardType, QVector<CardSlot>> bonusCount;
    for (int i = 0; i < PLAYING_SLOTS_COUNT; ++i)
    {
        if (playing_slots[i].size() > 0)
        {
            Card *top = playing_slots[i].back();
            if (top->isBonusCard())
            {
                bonusCount[top->cardType()].push_back({PlayingSlot, i});
            }
        }
    }

    for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
    {
        if (bonus_slots[i].size() == 1)
        {
            Card *top = bonus_slots[i][0];
            if (top->isBonusCard())
            {
                bonusCount[top->cardType()].push_back({BonusSlot, i});
            }
        }
    }

    for (auto iter = bonusCount.begin(); iter != bonusCount.end(); ++iter)
    {
        if (iter.value().count() == BONUS_COUNT)
        {
            bool canCollect = false;
            for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
            {
                if (bonus_slots[i].empty())
                {
                    canCollect = true;
                    break;
                }
                else if (bonus_slots[i][0]->cardType() == iter.key())
                {
                    canCollect = true;
                    break;
                }
            }
            if (!canCollect) continue;
            bonus_collect_buttons[iter.key()]->setStatus(BonusCollectButton::Active);
            bonus_cards_to_collect[iter.key()] = iter.value();
        }
    }
    return changed;
}

void CardTable::slotBonusButtonClicked(CardType type)
{
    if (is_playing_animations) return;
    QVector<CardSlot> cards = bonus_cards_to_collect[type];
    ParallelAnimationGroup *group = new ParallelAnimationGroup();
    group->setDelay(50);
    int index = -1;
    for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
    {
        if (bonus_slots[i].empty())
        {
            index = i;
            int z = 0;
            for (int j = 0; j < cards.size(); ++j)
            {
                collectCard(cards[j], {BonusSlot, i}, 80 + z++, group);
            }
            break;
        }
        else if (bonus_slots[i][0]->cardType() == type)
        {
            index = i;
            bonus_slots[i][0]->flip();
            int z = 1;
            for (int j = 0; j < cards.size(); ++j)
            {
                if (!(cards[j].type == BonusSlot && cards[j].index == i))
                {
                    collectCard(cards[j], {BonusSlot, i}, 80 + z++, group);
                }
            }
            break;
        }
    }
    if (group->size() > 0)
    {
        connect(group, &ParallelAnimationGroup::finished, this, [this, index, type, group]{
            releaseAnimationGroup(group);
            bonus_collect_buttons[type]->setStatus(BonusCollectButton::Down);
            for (int i = 0; i < bonus_slots[index].size(); ++i)
            {
                bonus_slots[index][i]->setZValue(i);
            }
            if (isFinished())
            {
                emit gameFinished();
                slotGameFinished();
            }
            else
            {
                autoCollect();
            }
        });
        startAnimationGroup(group);
    }
    else // nothing changed
    {
        delete group;
    }
}

void CardTable::slotGameFinished()
{
    is_playing_game = false;
    QPointF offset(0, background->boundingRect().height());

    ParallelAnimationGroup *groupB = new ParallelAnimationGroup();
    groupB->setDelay(60);
    for (int j = BONUS_COUNT - 1; j >= 0; --j)
    {
        for (int i = 0; i < BONUS_SLOTS_COUNT; ++i)
        {
            Animation *anim = moveAnimation(bonus_slots[i][j], cardSlotPosition({BonusSlot, i}) + offset,
                                            false, tweeny::easing::enumerated::cubicIn, 600, false);
            groupB->addAnimation(anim);
        }
    }
    ParallelAnimationGroup *groupF = new ParallelAnimationGroup();
    Animation *anim = moveAnimation(flower_card, cardSlotPosition({FlowerSlot, 0}) + offset,
                                    false, tweeny::easing::enumerated::cubicIn, 600, false);
    groupF->addAnimation(anim);
    ParallelAnimationGroup *groupS = new ParallelAnimationGroup();
    groupS->setDelay(60);
    for (int j = SUITED_COUNT - 1; j >= 0; --j)
    {
        for (int i = 0; i < SUITED_SLOTS_COUNT; ++i)
        {
            Animation *anim = moveAnimation(suited_slots[i][j], cardSlotPosition({SuitedSlot, i}) + offset,
                                            false, tweeny::easing::enumerated::cubicIn, 600);
            groupS->addAnimation(anim);
        }
    }
    connect(groupB, &ParallelAnimationGroup::finished, this, [this, groupB](){
        active_animation_groups.removeOne(groupB);
        groupB->deleteLater();
    });
    connect(groupF, &ParallelAnimationGroup::finished, this, [this, groupF](){
        active_animation_groups.removeOne(groupF);
        groupF->deleteLater();
    });
    connect(groupS, &ParallelAnimationGroup::finished, this, [this, groupS](){
        releaseAnimationGroup(groupS);
    });
    QTimer::singleShot(125, this, [this, groupB, groupF, groupS]{
        active_animation_groups.push_back(groupB);
        active_animation_groups.push_back(groupF);
        startAnimationGroup(groupS);
    });
}

double CardTable::intersectArea(const QRectF &a, const QRectF &b) const
{
    QRectF intersection = a.intersected(b);
    return intersection.width() * intersection.height();
}

void CardTable::moveCards(std::vector<Card*>& source, std::vector<Card*>& target, size_t num)
{
    auto itStart = source.end() - num;
    target.insert(target.end(), itStart, source.end());
    source.erase(itStart, source.end());
}

QPointF CardTable::cardSlotPosition(const CardSlot &slot)
{
    return cardSlotRect(slot).center();
}

int CardTable::suitedSlotIndex(CardType type)
{
    return suited_card_counter.slotIndex(type);
}

QPointF CardTable::suitedSlotPosition(CardType type)
{
    return cardSlotPosition({SuitedSlot, suitedSlotIndex(type)});
}

QRectF CardTable::cardSlotRect(const CardSlot &slot)
{
    switch (slot.type) {
    case PlayingSlot:
        return QRectF(40 + slot.index * 152, 279, 132, 243);
    case BonusSlot:
        return QRectF(39 + slot.index * 152 , 13, 132, 243);
    case SuitedSlot:
        return QRectF(800 + slot.index * 152, 13, 132, 243);
    case FlowerSlot:
        return QRectF(607, 13, 132, 243);
    default:
        return QRectF();
    }
}

QPointF CardTable::playingCardPosition(int slotIndex, int cardIndex)
{
    QPointF bottom = cardSlotRect({PlayingSlot, slotIndex}).center();
    return QPointF(bottom.x(), bottom.y() + cardIndex * 30);
}

Animation *CardTable::moveAnimation(Card *card, const QPointF &target, bool flip,
                                    tweeny::easing::enumerated easing, int duration, bool playSound)
{
    Animation *anim = new Animation(card, target, duration, easing);
    if (playSound)
    {
        QSoundEffect *effect = new QSoundEffect();
        effect->setSource(QUrl::fromLocalFile(":/sounds/card_sweep.wav"));
        effect->setVolume(0.6);
        connect(effect, &QSoundEffect::playingChanged, this, [effect](){
            if (!effect->isPlaying()) // 播放完毕
            {
                effect->deleteLater();
            }
        });
        anim->setSoundEffect(effect);
    }
    anim->setFlipBeforeStart(flip);
    return anim;
}

void CardTable::startAnimationGroup(AnimationGroup *group)
{
    is_playing_animations = true;
    active_animation_groups.push_back(group);
}

void CardTable::releaseAnimationGroup(AnimationGroup *group)
{
    is_playing_animations = false;
    active_animation_groups.removeOne(group);
    group->deleteLater();
}
